/*
 * Copyright (c) 2012 Aleksander Zotov, Benedikt Markt, Aliaksei Babkovich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 *  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 *  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 *  THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package simulation;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.concurrent.Callable;

import utils.ApplicationPreferences;
import utils.DateUtils;
import utils.Debug;
import database.SourceDB;

/**
 * This class represents a callable that monitors starts and monitors CAT.exe
 * 
 * @author Benedikt Markt
 * 
 */
public class SimulationRunner implements Callable<Integer> {
	private CallbackInterface progressCallback;
	private Simulation simulation;
	private int exitCode = 0;
	private int collisions = 0;

	public SimulationRunner(Simulation simulation) {
		this.simulation = simulation;
	}

	@Override
	public Integer call() {
		progressCallback.start();
		try {
			if (download()) {
				Thread.yield();
				writeCato(simulation);
				startCAT(simulation);
				return exitCode;
			} else {
				progressCallback.end();
				return 1000;
			}
		} catch (IOException e) {
			progressCallback.end();
			Debug.log(Debug.ERROR, "Error while running Simulation!");

		}
		progressCallback.end();
		return 999;
	}


	public void setInterface(CallbackInterface progressCallback) {
		this.progressCallback = progressCallback;
	}

	private Boolean startCAT(Simulation simulation) {
		try {
			File userDir = new File(".\\users\\" + simulation.getUsername()
					+ "\\");
			Runtime rt = Runtime.getRuntime();
			Process catProcess = rt.exec(".\\res\\CAT.exe", null, userDir);
			BufferedReader input = new BufferedReader(new InputStreamReader(
					catProcess.getInputStream()));
			String line = null;

			while ((line = input.readLine()) != null) {
				if (Thread.interrupted()) {
					catProcess.destroy();
					return false;
				}
				line = line.trim();
				if (line.endsWith("%")) {
					line = line.substring(25, line.length() - 2);
					line = line.trim();
					progressCallback.setProgress(Integer.parseInt(line));
				}
			}
			int exitCode = catProcess.waitFor();
			this.exitCode = exitCode;
			if (exitCode == 0) {
				Debug.log(Debug.INFO,
						"Simulation " + simulation.getIdentifier()
								+ " successfully completed!");
				Thread.sleep(200);
				progressCallback.setResults(readResults(), collisions);
				progressCallback.end();
				return true;
			} else {
				Debug.log(Debug.ERROR, "CAT.exe exited with error code "
						+ exitCode);
				progressCallback.end();
				return false;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		progressCallback.end();
		return false;
	}

	// TODO add to draft
	private void writeCato(Simulation simulation) {

		String dir = ".\\users\\" + simulation.getUsername() + "\\";
		File inCato = new File(dir + "input.cato");
		File refCato = new File(dir + "RefCatalog.cato");

		try {
			// writing simulation parameters to input.cato file
			FileWriter inCatoWriter = new FileWriter(inCato);

			DateUtils dU = new DateUtils();

			inCatoWriter.write(dU.toCatoString(simulation.getStartTime())
					+ ".0");

			inCatoWriter.write(System.getProperty("line.separator"));

			inCatoWriter.write(dU.toCatoString(simulation.getEndTime()) + ".0");

			inCatoWriter.write(System.getProperty("line.separator"));

			inCatoWriter.write('1');

			inCatoWriter.write(System.getProperty("line.separator"));

			inCatoWriter.write(simulation.getReferenceSystem());

			inCatoWriter.write(System.getProperty("line.separator"));

			inCatoWriter
					.write(Float.toString(simulation.getCollisionDistance()));

			inCatoWriter.write(System.getProperty("line.separator"));

			inCatoWriter.write("catalog.cato");

			inCatoWriter.write(System.getProperty("line.separator"));

			inCatoWriter.write("RefCatalog.cato");

			inCatoWriter.flush();
			inCatoWriter.close();

			// writing reference data to RefCatalog.cato
			FileWriter refCatoWriter = new FileWriter(refCato);

			for (String s : simulation.getRefDataArray()) {
				refCatoWriter.write(s.trim());
				refCatoWriter.write(System.getProperty("line.separator"));
			}
			refCatoWriter.flush();
			refCatoWriter.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private boolean download() throws IOException {
		ArrayList<SourceDB> sourceDBs = new ArrayList<SourceDB>();
		ApplicationPreferences prefs = ApplicationPreferences.getInstance();
		sourceDBs = prefs.getSourceDBs();

		// iterates through tle databases until a working one is found
		for (int i = 0; i < sourceDBs.size(); i++) {
			if (sourceDBs.get(i).isCorrectLogin()) {
				Debug.log(Debug.TRACE, "Source Database" + i
						+ " available, downloading...");
				sourceDBs.get(i).download(simulation.getUsername());
				return true;
			}
		}
		Debug.log(Debug.ERROR, "No Valid Source DBs found!");
		return false;
	}

	private String readResults() {
		String results = "";
		try {
			FileInputStream fInput = new FileInputStream(".\\users\\"
					+ simulation.getUsername() + "\\output.cato");
			DataInputStream in = new DataInputStream(fInput);
			BufferedReader reader = new BufferedReader(
					new InputStreamReader(in));

			String inLine;
			while ((inLine = reader.readLine()) != null) {
				if (inLine.trim().equals("Conjunction between:")) {
					collisions++;
				}
				results = results + inLine + "<br/>";
			}
			fInput.close();

		} catch (FileNotFoundException e) {
			Debug.log(Debug.ERROR, "Output file not found!");
		} catch (IOException e) {
			Debug.log(Debug.ERROR, "Error while reading output file!");
		}
		Debug.log(Debug.TRACE, "Collisions:" + collisions);
		return results;
	}
}
